// This File is part of the "nyaqt" Project
// 
// Copyright  2009 sometmes@gmail.com
// All rights reserved
// 
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.Text;
using SharpSvn;
using System.Collections.ObjectModel;
using System.IO;

namespace SvnWcUI
{
    public class WcReplace
    {
        string _source;

        public string Source
        {
            get { return _source; }
            set { _source = value; }
        }
        string _target;

        public string Target
        {
            get { return _target; }
            set { _target = value; }
        }

        SvnClient svn = new SvnClient();
        
        public void Exec()
        {
            // Verify target is a working copy
            bool wcready = false;
            if (SvnTools.IsManagedPath(this.Target))
            {
                //SvnStatusArgs args;
                Collection<SvnStatusEventArgs> status;
                bool b3 = svn.GetStatus(this.Target, out status);
                if (status.Count == 0)
                    wcready = true;
            }
            if (!wcready)
                throw new Exception("There are uncommited changes in the target working copy");

            DoReplace();
        }

        SvnStatusEventArgs FindStatus(Collection<SvnStatusEventArgs> entries, string path)
        {
            foreach (SvnStatusEventArgs entry in entries)
                if (entry.FullPath == path) return entry;

            return null;
        }

        private void DoReplace()
        {
            SvnClient svn = new SvnClient();
            Collection<SvnStatusEventArgs> entries;
            SvnStatusArgs statusArgs = new SvnStatusArgs();

            statusArgs.RetrieveRemoteStatus = false;
            statusArgs.RetrieveAllEntries = true;
            statusArgs.Depth = SvnDepth.Children;
            statusArgs.RetrieveIgnoredEntries = false;
            statusArgs.IgnoreExternals = true;


            int srcLen = this.Source.Length;
            int tgtLen = this.Target.Length;

            Stack<string> dirs = new Stack<string>();
            dirs.Push(this.Source);
            do
            {
                string dir = dirs.Pop() + "\\";

                svn.GetStatus(dir, statusArgs, out entries);
                entries.RemoveAt(0);
                foreach (SvnStatusEventArgs entry in entries)
                {
                    char noneType = '?';

                    if (entry.NodeKind == SvnNodeKind.None)
                    {
                        if (Directory.Exists(entry.FullPath))
                            noneType = 'D';
                        if (File.Exists(entry.FullPath))
                            noneType = 'F';
                    }

                    if (entry.NodeKind == SvnNodeKind.File || noneType == 'F')
                    {
                        string dest;
                        dest = Path.Combine(this.Target, entry.FullPath.Substring(srcLen + 1));
                        try
                        {
                            if (entry.LocalContentStatus == SvnStatus.Missing
                                || entry.LocalContentStatus == SvnStatus.Deleted)
                            {
                                if (File.Exists(dest))
                                {
                                    FileAttributes attr = File.GetAttributes(dest);
                                    attr = attr & ~FileAttributes.ReadOnly;
                                    File.SetAttributes(dest, attr);
                                    File.Delete(dest);
                                }
                            }
                            else if (File.Exists(dest))
                            {
                                FileAttributes attr = File.GetAttributes(dest);
                                attr = attr & ~FileAttributes.ReadOnly;
                                File.SetAttributes(dest, attr);
                                File.Copy(entry.FullPath, dest, true);
                            }
                            else
                            {
                                File.Copy(entry.FullPath, dest, true);
                                svn.Add(dest);
                            }
                        }
                        //catch (UnauthorizedAccessException)
                        //{
                        //    // apany per debug
                        //}
                        finally
                        {
                        }
                    }
                    else if (entry.NodeKind == SvnNodeKind.Directory || noneType == 'D')
                    {
                        dirs.Push(entry.FullPath);
                        string dest;
                        dest = Path.Combine(this.Target, entry.FullPath.Substring(srcLen + 1));
                        if (entry.LocalContentStatus == SvnStatus.Missing
                            || entry.LocalContentStatus == SvnStatus.Deleted)
                        {
                            if (Directory.Exists(dest))
                                Directory.Delete(dest, true);
                        }
                        else if (!Directory.Exists(dest))
                        {
                            Directory.CreateDirectory(dest);
                            svn.Add(dest);
                        }
                    }
                }

                // Delete Target files and directories not present on source
                List<string> todelete = new List<string>();
                string targetDir;
                targetDir = Path.Combine(this.Target, dir.Substring(srcLen + 1));
                Collection<SvnStatusEventArgs> targetEntries;
                svn.GetStatus(targetDir, statusArgs, out targetEntries);
                targetEntries.RemoveAt(0);
                foreach (SvnStatusEventArgs entry in targetEntries)
                {
                    string srcEntry;
                    srcEntry = Path.Combine(this.Source, entry.FullPath.Substring(tgtLen + 1));
                    if (FindStatus(entries, srcEntry) == null)
                        todelete.Add(entry.FullPath);
                }

                if (todelete.Count > 0)
                {
                    SvnDeleteArgs args = new SvnDeleteArgs();
                    args.Force = true;
                    svn.Delete(todelete, args);
                }
            }
            while (dirs.Count > 0);
        }
    }
}
